import Utils from './utils'
/**
 * Query类, 用于搜索leancloud数据.
 * @constructor
 * @param {string} className - 需要搜索的class名.
 */
class $Query {
  constructor(AV, className, request, that) {
    this.AV = AV
    this.className = className
    this.$http = className ? new AV.Query(className) : AV.Query
    this.request = request
    this.that = that
  }

  get(objectId) {
    if (!objectId) return
    const { $http, request, that } = this
    const dispatchRequest = () => $http.get(objectId)
    return request.call(that, dispatchRequest)
  }
  first() {
    const { $http, request, that } = this
    const dispatchRequest = () => $http.first()
    return request.call(that, dispatchRequest)
  }
  find() {
    const { $http, request, that } = this
    const dispatchRequest = () => $http.find()
    return request.call(that, dispatchRequest)
  }
  count() {
    const { $http, request, that } = this
    const dispatchRequest = () => $http.count()
    return request.call(that, dispatchRequest)
  }

  or(...arg) {
    if (!arg) return
    this.$http.or(...arg)
    return this
  }
  and(...arg) {
    if (!arg) return
    this.$http.and(...arg)
    return this
  }

  includeACL(isShow) {
    this.$http.includeACL(isShow)
    return this
  }

  include(keys) {
    if (!keys) return
    this.$http.include(keys)
    return this
  }

  // 精确查询
  equalTo(jsonObj) {
    if (!jsonObj) return
    Object.keys(jsonObj).forEach((item) =>
      this.$http.equalTo(item, jsonObj[item])
    )
    return this
  }
  notEqualTo(jsonObj) {
    if (!jsonObj) return
    Object.keys(jsonObj).forEach((item) =>
      this.$http.notEqualTo(item, jsonObj[item])
    )
    return this
  }
  sizeEqualTo(jsonObj) {
    if (!jsonObj) return
    Object.keys(jsonObj).forEach((item) =>
      this.$http.sizeEqualTo(item, jsonObj[item])
    )
    return this
  }

  // 关联查询
  matchesKeyInQuery(key, queryKey, query) {
    this.$http.matchesKeyInQuery(key, queryKey, query)
    return this
  }

  // 比较查询 > < >= <=
  lessThan(jsonObj) {
    if (!jsonObj) return
    Object.keys(jsonObj).forEach((item) =>
      this.$http.lessThan(item, jsonObj[item])
    )
    return this
  }
  lessThanOrEqualTo(jsonObj) {
    if (!jsonObj) return
    Object.keys(jsonObj).forEach((item) =>
      this.$http.lessThanOrEqualTo(item, jsonObj[item])
    )
    return this
  }
  greaterThan(jsonObj) {
    if (!jsonObj) return
    Object.keys(jsonObj).forEach((item) =>
      this.$http.greaterThan(item, jsonObj[item])
    )
    return this
  }
  greaterThanOrEqualTo(jsonObj) {
    if (!jsonObj) return
    Object.keys(jsonObj).forEach((item) =>
      this.$http.greaterThanOrEqualTo(item, jsonObj[item])
    )
    return this
  }

  exists(attributes) {
    //* 查找包含某一属性的对象
    this.$http.exists(attributes)
    return this
  }
  doesNotExist(attributes) {
    //* 查找不包含某一属性的对象
    this.$http.doesNotExist(attributes)
    return this
  }

  // 匹配查询
  startsWith(jsonObj) {
    for (const k in jsonObj) {
      this.$http.startsWith(k, jsonObj[k])
    }
    return this
  }
  contains(jsonObj) {
    for (const k in jsonObj) {
      this.$http.contains(k, jsonObj[k])
    }
    return this
  }
  containsAll(jsonObj) {
    for (const k in jsonObj) {
      this.$http.containsAll(k, jsonObj[k])
    }
    return this
  }
  containedIn(jsonObj) {
    if (!jsonObj) return
    Object.keys(jsonObj).forEach((item) =>
      this.$http.containedIn(item, jsonObj[item])
    )
    return this
  }
  notContainedIn(jsonObj) {
    if (!jsonObj) return
    Object.keys(jsonObj).forEach((item) =>
      this.$http.notContainedIn(item, jsonObj[item])
    )
    return this
  }
  matches(jsonObj) {
    if (!jsonObj) return
    Object.keys(jsonObj).forEach((item) =>
      this.$http.matches(item, jsonObj[item])
    )
    return this
  }
  matchesQuery(jsonObj) {
    if (!jsonObj) return
    Object.keys(jsonObj).forEach((item) =>
      this.$http.matchesQuery(item, jsonObj[item])
    )
    return this
  }
  doesNotMatchQuery(jsonObj) {
    if (!jsonObj) return
    Object.keys(jsonObj).forEach((item) =>
      this.$http.doesNotMatchQuery(item, jsonObj[item])
    )
    return this
  }

  // 排序查询
  ascending(key) {
    this.$http.ascending(key)
    return this
  }
  addAscending(key) {
    this.$http.addAscending(key)
    return this
  }
  descending(key) {
    this.$http.descending(key)
    return this
  }
  addDescending(key) {
    this.$http.addDescending(key)
    return this
  }

  // 翻页查询
  limit(size) {
    this.$http.limit(size)
    return this
  }
  skip(size) {
    this.$http.skip(size)
    return this
  }

  // ? 自定义接口----------
  /**
   * 获查询表数据 (行数据)
   * @param {Object} parmas - 查询值
   * @param {Number} parmas.page - 当前搜索第的页数, 默认为 1
   * @param {Number} parmas.start - 从第n项开始搜索, 默认为 0
   * @param {Number} parmas.limit - 搜索条数
   * @param {Object[]} parmas.sort - 排序项
   * @param {String} parmas.sort[].property - 按此key排序
   * @param {String} parmas.sort[].direction - 排序规则(descending | ascending)
   * @param {Object[]}  parmas.filter - 筛选项
   * @param {String}  parmas.filter[].property - 需要搜索的key值
   * @param {String}  parmas.filter[].operator - 筛选方法(具体查看leancloud数据开发指南查询相关)
   * @param {String}  [parmas.filter[].value] - 需要筛选的值
   * @param {Object} options - 查询参数
   * @param {Boolean} options.isCreate  是否创建一个搜索point用于关系查询 默认为 false
   * @param {Boolean} options.isGetCount  是否获取搜索总数 默认为 true
   * @param {String} options.$pointType  生成point的type属性名,默认为 pointType
   * @param {String} options.queryMethod  搜索方法 get|count|first|find 默认为 find (字段解释请参考leancloud文档)
   * @return {Array} LeanCloud数据对象数组
   *
   * @example
   * // 例子展示了按页面查询(page), 数量查询(limit), 排序(sort), 搜索name为'test'的对象(filter[1]), 搜索defaultCate为 '6131de50e2731473b2a9103b'(point)的对象
   * Query('Test').getData({page: 1, limit: 10, sort: [{ property: 'name', direction: 'descending' }], 
                            filter: [{ property: 'defaultCate', operator: 'include' }, 
                                      { property: 'name', operator: 'equalTo', value: 'test' },
                                      {property: 'defaultCate', operator: 'equalTo', value: {pointType: 'Cate_Banner', objectId: '6131de50e2731473b2a9103b',}}
                                    ]})
   */
  async getData(
    { page = 1, start = 0, limit, sort = [], filter = [] } = {},
    {
      isCreate = false,
      isGetCount = true,
      $pointType = 'pointType',
      queryMethod = 'find',
    } = {}
  ) {
    const { AV, className } = this
    const ASCE = 'ascending' // 升序
    const DESC = 'descending' // 降序
    const POINT_TYPE = $pointType
    const QUERY_METHOD = queryMethod

    let total = 0
    // 翻页部分
    if (limit) {
      // 翻页部分
      const skip = (page - 1) * limit + start
      this.limit(limit).skip(skip)
    }
    // 排序部分
    const asceKeys = sort.filter((item) => item.direction === ASCE)
    const descKeys = sort.filter((item) => item.direction === DESC)
    asceKeys.forEach((item, index) =>
      index === 0
        ? this.ascending(item.property)
        : this.addAscending(item.property)
    )
    descKeys.forEach((item, index) =>
      index === 0
        ? this.descending(item.property)
        : this.addDescending(item.property)
    )
    // 筛选部分
    filter.forEach(async (item) => {
      const { operator, property, value = null } = item
      switch (operator) {
        case 'exists':
        case 'doesNotExist':
        //
        case 'select':
        case 'include':
          this[operator](property)
          break
        case 'or':
        case 'and':
        //
        case 'equalTo':
        case 'notEqualTo':
        //
        case 'lessThan':
        case 'lessThanOrEqualTo':
        case 'greaterThan':
        case 'greaterThanOrEqualTo':
        //
        case 'startsWith':
        case 'contains':
        case 'matches':
        case 'containedIn':
        case 'notContainedIn':
        //
        case 'matchesQuery':
        case 'doesNotMatchQuery':
          if (value && Utils.isPlainObject(value)) {
            if (value[POINT_TYPE]) {
              const conditionKey = Object.keys(value).filter(
                (item) => item !== POINT_TYPE
              )
              const key = conditionKey[0]
              let point = null
              if (key === 'objectId')
                point = AV.Object.createWithoutData(
                  value[POINT_TYPE],
                  value[key]
                )
              if (point) this[operator]({ [property]: point })
            } else this[operator]({ [property]: value })
          } else this[operator]({ [property]: value })
          break
        case 'matchesKeyInQuery':
          this[operator](
            property,
            Object.keys(value)[0],
            value[Object.keys(value)[0]]
          )
          break
        case 'includeACL':
          this[operator](value)
        default:
          break
      }
    })
    if (isCreate) return this.$http

    // 获取统计数量
    if (isGetCount) {
      const count = await this.getData(
        { filter },
        { isGetCount: false, queryMethod: 'count' }
      )
      total = count.result
    }
    return new Promise((resolve, reject) => {
      this[QUERY_METHOD]()
        .then((result) => {
          const data = { result, total }
          resolve(data)
        })
        .catch((err) => reject(err))
    })
  }
}

export default $Query
